#include "Library_t.h"
#include "Book_t.h"
#include "Borrower_t.h"

#include <assert.h>
#include <sstream>

Library_t::Library_t()
{
 
}

Library_t::~Library_t()
{
 
	booksMap::const_iterator it1 = m_booksMap.begin();
	while(it1 != m_booksMap.end())
	{
		delete it1->second;
		it1++;
	}
	m_booksMap.erase(m_booksMap.begin(),m_booksMap.end());

	borrowersMap::const_iterator it2 = m_borrowersMap.begin();
	while(it2 != m_borrowersMap.end())
	{
		delete it2->second;
		it2++;
	}
	m_borrowersMap.erase(m_borrowersMap.begin(),m_borrowersMap.end());

}

bool Library_t::addBook(Book_t* const book)
{
	if(bookExists(book))
		return false;

	m_booksMap.insert(std::make_pair(book->isbn(), book));

	return true;
}

bool Library_t::removeBook(const Book_t* book, bool del)
{
	if(!bookExists(book))
		return false;
		
	if(!book->safeToRemove())
		return false;

	m_booksMap.erase(book->isbn());
	
	if(del)
		delete book;

	return true;
}

unsigned long Library_t::addBorrower(Borrower_t* const borrower)
{
	if(borrowerExists(borrower))
		return 0;

	m_borrowersMap.insert(std::make_pair(borrower->id(), borrower));

	return borrower->id();
}

bool Library_t::removeBorrower(const Borrower_t* borrower, bool del)
{
	if(!borrowerExists(borrower))
		return false;
	
	if(!borrower->safeToRemove())
		return false;

	m_borrowersMap.erase(borrower->id());
	
	if(del)
		delete borrower;

	return true;
}

bool Library_t::borrowerExists(const Borrower_t* borrower) const
{
	borrowersMap::const_iterator it = m_borrowersMap.find(borrower->id());
	
	return (it != m_borrowersMap.end());
}


bool Library_t::bookExists(const Book_t* book) const
{
	booksMap::const_iterator it = m_booksMap.find(book->isbn());

	return (it != m_booksMap.end());
}

Book_t* Library_t::findBook(ull ISBN) const
{
	booksMap::const_iterator it = m_booksMap.find(ISBN);

	if(it == m_booksMap.end())
		//return Book_t::invalid;
		return NULL;

	//return *it->second;
	return it->second;
}

Borrower_t* Library_t::findBorrower(unsigned long id) const
{
	borrowersMap::const_iterator it = m_borrowersMap.find(id);

	if(it == m_borrowersMap.end())
		//return Borrower_t::invalid;
		return NULL;

	//return *it->second;
	return it->second;
}

int Library_t::borrow(Borrower_t* const borrower, Book_t* const book)
{
	if(!bookExists(book) || !borrowerExists(borrower))
		return -1;

	// try to borrow the book
	int res=borrower->addBorrowed(book);
	if(res == 1)
		// book is available, so also update the book
		book->addBorrower(borrower);
	else if(res == 0)
		// book is not available, so add the borrower to the book's waiting list
		book->addWaitingBorrower(borrower);
	else
		// book is already borrowed by the borrower
		return -1;

	return res;
}

int Library_t::returnBook(Borrower_t* borrower, Book_t* book)
{
	if(!bookExists(book) || !borrowerExists(borrower))
		return -1;

	int res = borrower->returnBook(book);
	if(res == 1)
		// No one is waiting, the book can go available again
		book->returnBook(borrower);
	else if(res == 0)
		{
		// Someone is waiting for the book
		book->returnBook(borrower);

		// let the reader that waits to borrow the book
		// ***************** REMOVE ASSERT************************
		Borrower_t* tmpBorrower = book->whoWaits();
		tmpBorrower->decWaitingCounter();
		assert(borrow(tmpBorrower, book)==1);
		}
	else
		// book is not borrowed at all
		return -1;

	return res;
}

std::string Library_t::bookReport() const
{
	std::ostringstream os;
	booksMap::const_iterator it = m_booksMap.begin();
	while(it != m_booksMap.end())
	{
		os << *(it->second);
		it++;
	}

	os << "***************** End Book Report *****************" << std::endl;
	return os.str();
}

std::string Library_t::borrowerReport() const
{
	std::string str;
	borrowersMap::const_iterator it = m_borrowersMap.begin();
	while(it != m_borrowersMap.end())
	{
		str += (it->second)->borrowerReport();
		it++;
	}

	str += "***************** End Book Report *****************";

	return str;
}
